#
# Copyright 2008 Charles Connell <charles@connells.org>
#
# This file is part of Recuerde.
#
# Recuerde is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
# 
# Recuerde is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with Recuerde; if not, see <http://gnu.org/licenses/.>
#

import wx
import recuerde
from recuerde import List
from recuerde import Item
from database import Database

FIELDTYPE_STRING_STRING = 'Text'
FIELDTYPE_DATE_STRING = 'Date'
FIELDTYPE_CHOICE_STRING = 'Choice'

class AddListDialog(wx.Dialog):
    """Dialog box to create a new list."""
    def __init__(self, db, parent):
        wx.Dialog.__init__(self, parent, wx.ID_ANY, 'Add New List - Recuerde')
        """Creates the dialog box for adding a list."""

        # Remember database for later use in OnAdd
        self.db = db

        # Create new List object that we will be filling with fields
        self.newList = List()

        # Create new grid sizer to hold all of our widgets
        mainSizer = wx.GridBagSizer(0, 0)

        # Make a text control to enter the name of the list
        self.listNameCtrl = wx.TextCtrl(self, size=(-1, -1))
        # Make a label for that text control and add it to the sizer
        mainSizer.Add(wx.StaticText(self, label='List Name'), (0,0), (1,2), flag=wx.LEFT | wx.TOP, border=10)
        # Add the name text control to the sizer
        mainSizer.Add(self.listNameCtrl, (1,0), (1,2), flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border=10)
        # When text in the name text control changes, call OnListNameChange to enable or disable the lower Add button
        self.listNameCtrl.Bind(wx.EVT_TEXT, self.OnListNameChange)
        # Give focus to text control so user can start entering text into it right away
        self.listNameCtrl.SetFocus()

        # Create list view to hold list of fields
        self.listView = wx.ListView (self, style=wx.LC_REPORT | wx.LC_HRULES | wx.RAISED_BORDER, size=(-1, 150))
        # One column for the field name, one for the type
        self.listView.InsertColumn(0, 'Field', width=200)
        self.listView.InsertColumn(0, 'Type', width=200)
        # If item in list is selected or deselected, call OnListItemSelected to enable or disable the Delete button
        self.listView.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnListItemSelected)
        self.listView.Bind(wx.EVT_LIST_ITEM_DESELECTED, self.OnListItemSelected)
        # Add list view to sizer
        mainSizer.Add(self.listView, (2,0), (1,2), flag=wx.EXPAND | wx.ALL, border=10)

        # Create label to accompany the widgets for adding a new field, and add it to the sizer
        mainSizer.Add(wx.StaticText(self, label = 'Add New Field:'), (3,0), (1,1), flag=wx.EXPAND | wx.ALL, border=10)

        # Create sizer to hold Add and Delete buttons
        addRemoveSizer = wx.BoxSizer (wx.HORIZONTAL)
        # Add an Add button to it, and remember that button for later use in OnNameChange
        self.addFieldButton = wx.Button(self, wx.ID_ADD)
        # If button is clicked, run OnAddField
        self.addFieldButton.Bind(wx.EVT_BUTTON, self.OnAddField)
        # Add button to sizer
        addRemoveSizer.Add(self.addFieldButton, flag=wx.ALL, border=10)
        # Disable Add button because the name field is currently empty, and you cannot have a field with no name
        self.addFieldButton.Disable()

        # Add an Delete button to it, and remember that button for later use in OnNameChange
        self.removeFieldButton = wx.Button(self, wx.ID_DELETE)
        # If button is clicked, run OnRemoveField
        self.removeFieldButton.Bind(wx.EVT_BUTTON, self.OnRemoveField)
        # Add button to sizer
        addRemoveSizer.Add(self.removeFieldButton, flag=wx.ALL, border=10)
        # Disable Add button because there is currently no item selected to delete
        self.removeFieldButton.Disable()
        # Add the sizer with Add and Delete buttons to the main sizer
        mainSizer.Add(addRemoveSizer, (3,1), (1,1))

        # Create text control for the new field's name
        self.nameCtrl = wx.TextCtrl(self, size=(200, -1))
        # Create accompanying label and add it to the sizer
        mainSizer.Add (wx.StaticText(self, label='Name'), (4,0), (1,1), flag=wx.LEFT, border=10)
        # Add text control to sizer
        mainSizer.Add (self.nameCtrl, (5,0), (1,1), flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border=10)
        # If text control changes, call OnNameChange to enable / disable the upper Add button
        self.nameCtrl.Bind(wx.EVT_TEXT, self.OnNameChange)

        # Create combobox for the new field's type
        self.typeCtrl = wx.ComboBox(self, size=(200, -1), style=wx.CB_READONLY, choices=(FIELDTYPE_STRING_STRING, FIELDTYPE_DATE_STRING, FIELDTYPE_CHOICE_STRING), value=FIELDTYPE_STRING_STRING)
        # Create accompanying label and add it to the sizer
        mainSizer.Add (wx.StaticText(self, label='Type'), (4,1), (1,1), flag=wx.LEFT, border=10)
        # Add combobox to sizer
        mainSizer.Add (self.typeCtrl, (5,1), (1,1), flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border=10)
        # If combobox changes, call OnNameChange to enable / disable the choices text control
        self.typeCtrl.Bind(wx.EVT_TEXT, self.OnTypeChange)

        # Create text control for the choices for a field of type Choice
        self.choicesCtrl = wx.TextCtrl(self, size=(200, -1))
        # Create accompanying label and add it to the sizer
        self.choicesCtrlLabel = wx.StaticText(self, label='List of choices separated by commas (no spaces after commas)')
        mainSizer.Add (self.choicesCtrlLabel, (6,0), (1,2), flag=wx.LEFT, border=10)
        # Add text control to sizer
        mainSizer.Add (self.choicesCtrl, (7,0), (1,2), flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border=10)
        # If text control changes, call OnChoicesChange to enable / disable the upper Add button
        self.choicesCtrl.Bind(wx.EVT_TEXT, self.OnChoicesChange)
        # Disable control because Choice is not the currently selected field type
        self.choicesCtrl.Disable()
        # Disable the label as well
        self.choicesCtrlLabel.Disable()

        # Create label that can display error messages to the user. It is currently empty and therefore invisible.
        self.errorCtrl = wx.StaticText(self)
        # Add that to the sizer under the last widget
        mainSizer.Add(self.errorCtrl, (8,0), (1,2), flag=wx.EXPAND | wx.ALL, border=10)

        # Create sizer to hold this dialog's Add and Cancel buttons. Cancel button comes with the returned sizer.
        buttonSizer = self.CreateButtonSizer(wx.CANCEL)
        # Add an Add button to it, and remember that button for later use in OnListNameChange
        self.addButton = wx.Button(self, wx.ID_ADD)
        # If button is clicked, run OnAdd
        self.addButton.Bind(wx.EVT_BUTTON, self.OnAdd)
        # Add button to sizer
        buttonSizer.Add (self.addButton, flag=wx.EXPAND | wx.LEFT | wx.RIGHT, border=10)
        # Add sizer with Add and Cancel buttons to main widget sizer
        mainSizer.Add(buttonSizer, (9,0), (1,2), flag=wx.EXPAND | wx.ALL, border=10)
        # Disable Add button because the name field is currently empty, and you cannot have a list with no name
        self.addButton.Disable()

        # Distribute widgets nicely
        self.SetSizer(mainSizer)
        mainSizer.Fit(self)

        # Put the in-memory list fields on the list view
        self.RefreshList()

    def OnAddField(self, event):
        """Get value from text control and add it to in-memory list."""
        # Add string field with name from name text control
        if self.typeCtrl.GetValue() == FIELDTYPE_STRING_STRING:
            self.newList.AddField(recuerde.FIELDTYPE_STRING, self.nameCtrl.GetValue())
        # Add date field with name from name text control
        elif self.typeCtrl.GetValue() == FIELDTYPE_DATE_STRING:
            self.newList.AddField(recuerde.FIELDTYPE_DATE, self.nameCtrl.GetValue())
        # Add combo field with name from name text control and choices from choice text control, split around commas
        elif self.typeCtrl.GetValue() == FIELDTYPE_CHOICE_STRING:
            self.newList.AddField(recuerde.FIELDTYPE_CHOICE, self.nameCtrl.GetValue(), self.choicesCtrl.GetValue().split(','))
        # Put the in-memory list fields on the list view
        self.RefreshList()

    def OnRemoveField(self, event):
        """Removes the selected field from the list of fields."""
        # Get a copy of the fields in the list
        fields = self.newList.GetFields()
        # Get the index of the one selected, then subtract one because the first one in the list view is not in the real list
        fieldIndex = self.listView.GetFirstSelected() - 1
        # Remove the field from the list
        del fields[fieldIndex]
        # Put the new fields back in the list
        self.newList.SetFields(fields)
        # Refresh the list
        self.RefreshList()
        # Disable the remove button, since there is nothing currently selected to delete
        self.removeFieldButton.Disable()

    def OnNameChange(self, event):
        """If there is text in the field name text control, enable the upper Add button. Otherwise, disable it."""
        if len(self.nameCtrl.GetValue()):
            self.addFieldButton.Enable()
        else:
            self.addFieldButton.Disable()

    def OnTypeChange(self, event):
        """If currently chosen type is Choice, enable and clear choices text control. Otherwise, disable it."""
        if self.typeCtrl.GetValue() == FIELDTYPE_CHOICE_STRING:
            # Enable control and label
            self.choicesCtrl.Enable()
            self.choicesCtrlLabel.Enable()
            # Clear it from last use
            self.choicesCtrl.Clear()
            # Disable upper Add button; since there are no choices, you cannot add the field yet
            self.addFieldButton.Disable()
        else:
            # Disable control and label
            self.choicesCtrl.Disable()
            self.choicesCtrlLabel.Disable()

    def OnListItemSelected(self, event):
        """If a list item has been selected that is *not* the first one, enable the Delete button. Otherwise, disable it."""
        if self.listView.GetFirstSelected() > 0:
            self.removeFieldButton.Enable()
        else:
            self.removeFieldButton.Disable()

    def OnListNameChange(self, event):
        """If the list name control has text in it, enable the lower Add button. Otherwise, disable it."""
        if self.listNameCtrl.GetValue():
            self.addButton.Enable()
        else:
            self.addButton.Disable()

    def OnChoicesChange(self, event):
        """If the choices text control has text in it, enable the upper Add button. Otherwise, disable it."""
        if self.choicesCtrl.GetValue():
            self.addFieldButton.Enable()
        else:
            self.addFieldButton.Disable()

    def RefreshList(self):
        """Fill list view with representations of fields in new list."""
        # Clear list so we can start fresh
        self.listView.DeleteAllItems()
        # The first item is always "Name"
        self.listView.Append(('Name', 'Text'))
        # For each field in the list
        for field in self.newList.GetFields():
            # If type is string, add field name and field type string
            if field[0] is recuerde.FIELDTYPE_STRING:
                self.listView.Append([field[1], FIELDTYPE_STRING_STRING])
            # If type is date, add field name and field type string
            elif field[0] is recuerde.FIELDTYPE_DATE:
                self.listView.Append([field[1], FIELDTYPE_DATE_STRING])
            # If type is choice, add field name, field type string, and available choices in brackets
            elif field[0] is recuerde.FIELDTYPE_CHOICE:
                typeString = FIELDTYPE_CHOICE_STRING + ' [' + ','.join(field[2]) + ']'
                self.listView.Append([field[1], typeString])

    def OnAdd(self, event):
        """Reads the information from the dialog and saves the list."""

        # Find lists with duplicate a name
        duplicate = filter(lambda list: list.GetName() == self.listNameCtrl.GetValue(), self.db.GetLists())

        # If there is a duplicate, tell user through error label and do nothing else
        if (len(duplicate) is not 0):
            self.errorCtrl.SetLabel('There is already a list named "%s", please choose another name.' % listNameCtrl.GetValue())

        # Otherwise, proceed
        else:
            # Set the list's name from the name text control
            self.newList.SetName(self.listNameCtrl.GetValue())

            # Write this list to the database, and add it to the in-memory list
            self.db.AddList(self.newList)

            # Close this dialog
            self.EndModal (wx.ID_ADD)

